﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.Model;
using Common.Interfaces;
using System.ServiceModel;
using System.Windows.Forms;
using Common.Extensions;
using System.ServiceModel.Channels;
using System.Net;
using System.Diagnostics;
using System.IO;

namespace JOthello_SRV
{
    [ServiceBehaviorAttribute(IncludeExceptionDetailInFaults = true)]
    public class SRVManager : Common.Interfaces.IJOthello
    {
        private static readonly string kProtokoll = "Protokoll";
        private static readonly string kSpielfeld = "Feld";

        private static Dictionary<string, Dictionary<string, object>> spiele = new Dictionary<string, Dictionary<string, object>>();
        private static List<CSpieler> registeredSpieler = new List<CSpieler>();
        private static Dictionary<IJOthelloCallback, CSpieler> callbacks = new Dictionary<IJOthelloCallback, CSpieler>();

        #region service things
        private IJOthelloCallback currentCallback
        {
            get
            {
                return OperationContext.Current.GetCallbackChannel<IJOthelloCallback>();
            }
        }
        private static IJOthelloCallback getCallback(CSpieler spieler)
        {
            try
            {
                return callbacks.First((callback) => callback.Value.Equals(spieler)).Key;
            }
            catch (Exception)
            {
                return null;
            }
        }
        private string currentIP
        {
            get
            {
                return ((RemoteEndpointMessageProperty)OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name]).Address; ;
            }
        }

        public bool register(CSpieler spieler)
        {
            if (!registeredSpieler.Contains(spieler) &&
                !Properties.Settings.Default.BlacklistIP.Contains(currentIP) &&
                !Properties.Settings.Default.BlacklistNames.Contains(spieler.Name))
            {
                registeredSpieler.Add(spieler);
                Output.WriteLine("Spieler registriert: " + spieler.ToString(true), true, true);
                return true;
            }
            return false;
        }
        public void unregister(CSpieler spieler)
        {
            registeredSpieler.Remove(spieler);
            Output.WriteLine("Spieler nicht mehr registriert: " + spieler.ToString(true), true, true);
        }
        public void connect(CSpieler spieler)
        {
            IJOthelloCallback callback = currentCallback;
            if (!callbacks.ContainsKey(callback))
            {
                callbacks.Add(callback, spieler);
            }

            Output.WriteLine("Client connected: " + Dns.GetHostEntry(currentIP).HostName + "(" + currentIP + ")", true, true);
        }
        public void disconnect(CSpieler spieler)
        {
            callbacks.Remove(currentCallback);

            Output.WriteLine("Client disconnected: " + Dns.GetHostEntry(currentIP).HostName + "(" + currentIP + ")", true, true);
        }

        public bool playAgainst(CSpieler me, CSpieler opponent)
        {
            Output.WriteLine(me.Name + " möchte gegen " + opponent.Name + " spielen.", true, true);
            if (registeredSpieler.Contains(me) && registeredSpieler.Contains(opponent))
            {
                try
                {
                    getCallback(opponent).askToPlay(me);
                }
                catch (Exception)
                {
                    unregister(opponent);
                    disconnect(opponent);
                    return false;
                }
                return true;
            }
            return false;
        }

        public void acceptPlayAgainst(bool accepted, CSpieler me, CSpieler opponent)
        {
            Output.WriteLine(me.Name + " hat die Spielaufforderung gegen " + opponent.Name + (accepted ? " angenommen." : " abgelehnt"), true, true);
            getCallback(opponent).playAccepted(accepted, me);
        }

        public void startGame(CSpieler me, CSpieler opponent, int size, bool showPossible, CSpielfeld.GameMode m)
        {
            IJOthelloCallback meCallback = getCallback(me);
            IJOthelloCallback opponentCallback = getCallback(opponent);
            if (me.Farbe == opponent.Farbe)
            {
                callbacks[getCallback(opponent)].Farbe = opponent.Farbe = (CSpielfeld.FieldColor)(((int)opponent.Farbe) * -1);
            }
            
            CSpielfeld.Instance.reInit(m, size);
            
            string spielID = Guid.NewGuid().ToString();
            spiele.Add(spielID, (new Dictionary<string,object>(){
                { kSpielfeld , new CSpielfeld(CSpielfeld.Instance)},
                { kProtokoll ,  new CProtokoll(new List<CSpieler>(){me, opponent}, showPossible) }
            }));

            if (Output.isLogging)
            {
                CProtokoll currentP = ((CProtokoll)spiele[spielID][kProtokoll]);
                currentP.Pfad = Path.Combine(Output.LogDir, spielID + CProtokoll.suffix);
                currentP.AddEintrag(((CSpielfeld)spiele[spielID][kSpielfeld]), me);
                currentP.export();
            }

            meCallback.startGame(spielID, true, opponent, me.Farbe, size, showPossible, m);
            opponentCallback.startGame(spielID, false, me, opponent.Farbe, size, showPossible, m);

            unregister(me);
            unregister(opponent);

            Output.WriteLine("Das Spiel("+ spielID +") " + me.ToString(true) + " gegen " + opponent.ToString(true) + " hat begonnen. (" + size.ToString() + " | " + m.ToString() + ")", true, true);
        }

        public void turn(string SpielID, CSpieler me, CPoint p, CSpieler opponent)
        {
            getCallback(opponent).gotTurn(me, p);
            Output.WriteLine(me.Name + " hat gegen " + opponent.Name + " an " + p.ToString(true) + " gelegt.", true, true);

            if (Output.isLogging)
            {
                CSpielfeld feld = (CSpielfeld)spiele[SpielID][kSpielfeld];
                CProtokoll protokoll = (CProtokoll)spiele[SpielID][kProtokoll];
                feld.reCalculatePossibleFields(me);
                feld.turn(p, me);
                protokoll.AddEintrag(feld, me);
                protokoll.export();
            }
        }

        public List<CSpieler> getRegistered()
        {
            return registeredSpieler;
        }

        public void cancelGame(string SpielID, CSpieler me, CSpieler opponent)
        {
            try
            {
                getCallback(opponent).gameCanceled();
                if (spiele.ContainsKey(SpielID))
                {
                    Dictionary<string, object> theGame = spiele[SpielID];
                    spiele.Remove(SpielID);
                    if (Output.isLogging) ((CProtokoll)spiele[SpielID][kProtokoll]).export();
                }
            }
            catch (Exception) { return; }
        }
        #endregion

        public static List<CSpieler> getRegisteredSpieler()
        {
            return registeredSpieler;
        }

        /// <summary>
        /// kickt einen spieler
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static bool kick(string name)
        {
            try
            {
                IJOthelloCallback callback = null;
                
                CSpieler sp = registeredSpieler.First((s) => s.Name == name);
                if (sp != null)
                {
                    registeredSpieler.Remove(sp);
                    callback = getCallback(sp);
                }

                callback = callbacks.First((item) => item.Value.Name == name).Key;
                
                if (callback != null)
                {
                    callbacks.Remove(callback);
                    callback.gotKicked();
                    return true;
                }

                return false;
            }
            catch (Exception ex)
            {
                Debug.Print(ex.Message);
                Debug.Print(ex.StackTrace);
                return false;
            }
        }
    }
}
